//GraphicsInterface.h
//Created 25/11/13
//Created By Daniel Bowler
//
//This is the main class that the programmer will interact with for rendering
//purposes. Simply put, the scene programmer will pass all the models
//that have been creates (or is active) to this class and then calls
//Render(). This kicks off the rendering (Frustum culling, shadow map
//generation, SSAO map generation, colour writes etc). 

#pragma once

//For D3D resources. 
#include "d3dUtil.h"

//So classes that include this has access to the SceneBoundingSphere struct
#include "DirecLightShadowMapData.h"

//Define how large the shadow map will be in pixels - should match values
//found in TileForwardShadowMappingGlobals.fx
#define SHADOW_MAP_RESOLUTION 4096
//Radius of the shadow map - how much of the map do we want in the shadow map - 
//Should be >= than FRAGMENT_SHADOW_TEST_RANGE in TileForwardShadowMappingGlobals.fx
#define SHADOW_MAP_TRANSLATION 300.0f

//If uncommented, then semi-transparent geometry will be rendered
//to the shadow map. Else, only opaque will.
#define SHADOW_MAP_DRAW_TRANSPARENT_GEOMETRY

//if uncommented, enable SSAO
#define ENABLE_SSAO
//How many times to blur the SSAO Map. The more blur passes the better the
//results but at the cost of performance
#define SSAO_MAP_BLUR_COUNT 1

//Forward declare the manager classes we use. We will interact with these class
//extensivly.
class GraphicsManager;
class D3D11Renderer;

//Forward declare our cascaded shadow map class. 
class CSM;

//Forward declare Virtual Camera. Each scene has one of these instances and the
//renderer needs to know which one will be used (Updated per frame incase
//we have some cinematics where the player cam will change)
class VirtualCamera;

//Forward declare our Model classes. 
class StaticModel;
class StaticInstancedModel;
class AnimatedModel;
//Forward declare abstract advanced model class. 
class AbstractAdvancedModel;

//Forward declare SkyDome
class SkyDome;

//Forward declare SSAOData - this class contains all the information
//required to generate a SSAOMap. 
class SSAOData;

class GraphicsInterface
{
public:
	//Class is a singleton
	static GraphicsInterface* GetInstance();
	//Release at the end of the application life cycle
	void Release();

public:
	//When the window changes size, the application will call this function. 
	//It will be used to destroy and recreate screensize render targets (eg,
	//shadow map). Aplication class will have the up to date renderd target
	//dimentions. 
	void OnResize();

public:
	//Useful functions that interact with the low level graphics
	//system and are outside the scope of other function sections (public: private:
	//etc) in this class
	//
	//Returns the aspect ratio of the backbuffer. Used when you want
	//to init or resize the cameras lens. 
	float ReturnBackBufferAspectRatio();

	//Size of the render target in pixels
	float ReturnRenderTargetHeight();
	float ReturnRenderTargetWidth();

	//Returns true if in full screen mode - Good way to decide if we should
	//enable vsync.
	bool IsInFullScreenMode();

	//Sets MSAA - true turns it on, false turns it off. Can be called
	//at run time.
	void Set4XMSAA(bool state);

	//Set shadow mapping state - true and we will draw to the shadow map
	//(if available). 
	void SetIsShadowMappingEnabled(bool state){isShadowMappingEnabled = state;};

	//Returns the D3D11 Device and context. The device is required
	//to create buffers/textures. The context is for state changes. 
	//
	//Both often passed to models, skyboxes etc. when creating. 
	ID3D11Device* GetDevice();
	ID3D11DeviceContext* GetContext();

public:
	//Returns the current active SkyDome
	SkyDome* GetCurrentSkyDome(){return skyDome;};

public:
	//Returns the Debug draw quad VB and IB
	ID3D11Buffer* ReturnDebugDrawQuadVB(){return debugDrawQuadVB;};
	ID3D11Buffer* ReturnDebugDrawQuadIB(){return debugDrawQuadIB;};

public:
	//Tile Based Forward Rendering Debug Functions
	//
	//Renders the depth buffer filled after the Z Pre Pass. 
	void DrawZPrePassFilledDepthBuffer();
	//Samples the Maz Z buffer. Each pixel in a tile will be the same colour. 
	void DrawTileMaxZBuffer();
	//Passes the LightListStartEndBuffer - This will show us how many lights have been 
	//assigned to each tile. This function will render in greyscale.  
	void DrawLightListStartEndBufferGreyscale();
	//This is as above, but using differnt colours to represent how many lights
	//have been assigned to a tile. This is less accurate than above, but easier
	//to see areas of high and low density.
	void DrawLightListStartEndBufferColour(); 
	//Like the above two functions, but shows the LLSEB(T) - Ie, how many lights
	//are affecting a tile for transparant geometry. 
	void DrawLightListStartEndBufferTransparantGreyscale();
	void DrawLightListStartEndBufferTransparantColour();

	//Other debug drawing:
	//
	//SSAO:
	//
	//SSAO View Space depth/normal texture 
	void DrawSSAOViewNormals();
	void DrawSSAOViewDepths();
	void DrawSSAOMap();

	//Draw depth map (Shadow mapping)
	void DrawShadowMapDepth();

public:
	//Functions that the scene programer will be interested in.
	//
	//Set scene point and spot lights. Bool should be set to true if you want
	//to visualise the lights (Debugging/coolness). Called in the scenes OpenScene()
	//function and left alone. Use UpdateSceneLights() if you want to animate any
	//light. 
	void SetSceneLights(TileBasedLight* lightArray, unsigned count, bool shouldCreateVisBuffers);
	//Update point and spot lights. Very slow > 200 odd lights. 
	void UpdateSceneLights(TileBasedLight* newLightArray, bool shouldCreateVisBuffers);
	//Sets/Updates (eg moves) directional lights - Max 3. 
	void SetDirectionalLights(DirectionalLight* dLightArr, unsigned dLightCount, 
		ShadowMapBoundingSphere* sceneVolume = NULL);
	
	//Set the skydome - optional.
	void SetCurrentSkyDome(SkyDome* skyDome);
	//Sets the hemispherical ambient scene data. Up represents the light from
	//above the object and down represents the light from under it. For example, 
	//if you have a blue sky and a green grassy floor, set downColour to green
	//and the upColour to blue - Make sure to modify models ambient reflectivity
	//as this does ALOT to a models final colour!
	//
	//Default is black and white which does a good job to brighten up the scene
	//but keeping the shaded areas of a model dark (those pointing down) - Best in
	//outdoor scenes - Indoor you should set yourself manually per room!
	void SetHemisphericAmbientColours(XMFLOAT3 downColour, XMFLOAT3 upColour);

	//Called at the top of each scenes RenderScene function. Essentially, 
	//clears the old data stored within this class and clears the render
	//target (backbuffer) and depth buffer thus setting the applicaiton up
	//for a new frame of rendering. Make sure to pass the playerCamera for this!
	//
	//The framework that has been built has a camera per scene... pass this, unless
	//you have a Player class which has a camera itself. Passing NULL would
	//be very silly indeed.
	//
	//Modified: 14/05/14 - Added bool as paramater. Default is true which will
	//go ahead and allow the graphics interface to update the shadow map. Pass
	//false if you will update the shadow map manually (eg: in the demo scenes)
	void StartFrame(const float* backBufferClearColour, VirtualCamera* playerCamera, 
		bool shouldUpdateShadowMap = true);
	//Render. Called after every object has been added to the batch. This will
	//automatically preform frustum culling, hardware instancing, shadow mapping, 
	//light culling and post processing. 
	//
	//ShouldDrawTransparantObjects when false will NOT draw transparant objects
	//in the scene. I will be using this for debug purposes.
	void Render(bool debugDrawLights, bool shouldDrawTransparantObjects = true);
	//Called to present the backbuffer - done after any rendering and post 
	//processing. Will be the last function called in each scenes
	//rendering function.
	//
	//Pass true to enable/disable vsync. Generally, true when it full screen
	//otherwise false (Ie windowed mode)
	void EndFrame(bool vysnc = false);
	
	//Adds models to the 'batch' - List of all objects within a scene
	void AddModelToBatch(StaticModel* model);
	void AddModelToBatch(StaticInstancedModel* model);
	void AddModelToBatch(AnimatedModel* model);
	void AddModelToBatch(AbstractAdvancedModel* model);

public:
	//Once we have rendered the 3D scene, we may wish to overlay some 2D stuff (text, 
	//sprites) for the HUD. These functions will set up D3D in order to allow us to
	//do it. 
	//
	//NOTE: As suggested above, the 2D rendering (hud for example) should be done 
	//AFTER rendering (IE, calling GraphicsInterface::Render() the 3D scene (Thus overlaying the HUD). 
	//
	//Called when we want to render 2D sprites using the sprite batch. 
	void Begin2DRendering();
	//Once finished rendering the 2D sprites, we call this function. 
	void End2DRendering();
	
private:
	//Reference to the manager classes required for this class to do
	//its work.
	GraphicsManager* sharedGraphicsManager;
	//This class creates and manages the RendererD3D11 class. Essentially, a class
	//we will delegate the renderering too (interacting with shaders which are
	//NOT part of the tile based system)
	D3D11Renderer* sharedD3D11Renderer;

	//We need to store which camera will be used to render these pieces of 
	//gemetry for this frame. Generally, this will be the player camera
	//which will be the one each scene has attached. 
	VirtualCamera* playerCamera;

	//Class stores Directional Lights (up to MAX_DIRECTIONAL_LIGHT_COUNT (See graphics
	//manager.h for this limit - should be 3) and the count. 
	DirectionalLight* dLightArr;
	unsigned dLightCount;

	//Hemispheric ambient colours
	XMFLOAT3 hemiDownColour;
	XMFLOAT3 hemiUpColour;
	//Up-Down
	XMFLOAT3 hemiColRange;

	//Store a skydome. Graphics interface will handle rendering it. 
	SkyDome* skyDome;

	//Currently used environment map - generally (and initially limited to)
	//this will be the skyDome we use. 
	ID3D11ShaderResourceView* activeEnvMap;

private:
	//Debug draw quad. 
	ID3D11Buffer* debugDrawQuadVB;
	ID3D11Buffer* debugDrawQuadIB;

private:
	//##########################################################################
	//############################Effects#######################################
	//##########################################################################
	//
	//SSAO Data - encapsulates all the data required for SSAO.
	SSAOData* ssaoData;

	//Should we draw to the shadow map?
	bool isShadowMappingEnabled;

	//Directional light shadow map - the 0th directional light in the scene
	//will cast shadows
	DirecLightShadowMapData* dLightZeroShadowMapData;

	//Holds our cascaded shadow maps. Helper functions will exists to bind
	//each shadow map. 
	CSM* csmData;

private:
	//'Batches'. List of objects in the scene. These are the lists pre culling.
	std::vector<StaticModel*>staticModelBatch;
	std::vector<StaticInstancedModel*>staticInstModelBatch;
	std::vector<AnimatedModel*>animModelBatch;
	std::vector<AbstractAdvancedModel*>advModelBatch;

	//Vectors that will be returned from frustum culling functions. This represents
	//the list of objects that passes the frustum culling test (ie, are within the
	//bounds of the camera and therefore should be rendered)
	std::vector<StaticModel*>staticModelsToRender;
	//For instanced models - anything in this vector has at least one instance to
	//render. If the frustum culling function decides that no instances will
	//be rendered, it will not be added to this list.
	std::vector<StaticInstancedModel*>staticInstModelsToRender;
	//Animated models that have passed the frustum test. 
	std::vector<AnimatedModel*>animModelsToRender;
	//Advanced models that pass the frustum test. 
	std::vector<AbstractAdvancedModel*>advModelsToRender;

private:
	//Function that preforms frustum culling on batches. Returns a filled vector
	//of StaticModels pointers that pass the frustum culling test (IE, should be
	//rendereed)
	void PreformStaticModelFrustumCulling(
		std::vector<StaticModel*> &staticModelsInput,
		std::vector<StaticModel*> &staticModelsPass);
	//Preforms frustum culling on a batch of static instanced models. Handled
	//differntly than above -preform the frustum test on each instance
	//and build the instance data vertex buffer(World matrix of each 
	//instance that passes the frustum test)
	//
	//Returns a list of StaticInstancedModels that have at least
	//one instance that passes the frustum test. 
	void PreformStaticInstancedModelFrustumCulling(
		std::vector<StaticInstancedModel*> &staticInstancedModelInput,
		std::vector<StaticInstancedModel*> &staticInstancedModelpass);
	//Preforms frustum culling on a batch of animated models. 
	void PreformAnimatedModelFrustumCulling(
		std::vector<AnimatedModel*> &animModelsInput,
		std::vector<AnimatedModel*> &animModelsPass);
	//Preforms frustum culling on a batch of advanced models. 
	void PreformAdvancedModelFrustumCulling(
		std::vector<AbstractAdvancedModel*> &advModelsInput, 
		std::vector<AbstractAdvancedModel*> &advModelsPass);

private:
	//Sorts all transparent geometry that passes the frustum culling test
	//(Furthest to closest) - This is on the TODO list. 
	void SortGeometry(std::vector<StaticModel*>&staticModels, 
		std::vector<StaticInstancedModel*>&staticInstModels, 
		std::vector<AnimatedModel*>&animModels, 
		std::vector<AbstractAdvancedModel*>&adModels);

private:
	//Functions that render geometry that pass the frustum culling test. (By reference
	//for speed)
	void DrawStaticModels(std::vector<StaticModel*> &modelsToRender, 
		bool zPrePass, bool isOpaquePass, bool isShadowPass);
	void DrawStaticInstancedModels(std::vector<StaticInstancedModel*> &modelsToRender,
		bool zPrePass, bool isOpaquePass, bool isShadowPass);
	void DrawAnimatedModels(std::vector<AnimatedModel*> &modelsToRender,
		bool zPrePass, bool isOpaquePass, bool isShadowPass);
	void DrawAdvancedModels(std::vector<AbstractAdvancedModel*> &modelsToRender, 
		bool zPrePass, bool isOpaquePass, bool isShadowPass);

	//Draws a single static model (Well... SubModel anyway) - mIndex is the subModel
	//index
	void DrawStaticModelTextureMapping(StaticModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticModelNormalMapping(StaticModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticModelDisplacementMapping(StaticModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticModelPlainColour(StaticModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);

	//Draws static instanced models
	void DrawStaticInstancedModelNormalMapping(StaticInstancedModel* mod, unsigned mIndex, 
		bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticInstancedModelTextureMapping(StaticInstancedModel* mod, unsigned mIndex,
		bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticInstancedModelSolidColour(StaticInstancedModel* mod, unsigned mIndex, 
		bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawStaticInstancedModelDisplacementMapping(StaticInstancedModel* mod, unsigned mIndex,
		bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);

	//Draws animated models. 
	void DrawAnimatedModelNormalMapping(AnimatedModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawAnimatedModelTextureMapping(AnimatedModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);
	void DrawAnimatedModelSolidColour(AnimatedModel* mod, unsigned mIndex, bool zPrePass,
		bool isShadowPass, CXMMATRIX &view, CXMMATRIX &proj);

private:
	//Updates the shadow map - manually. We use this to provide a simple
	//method to have the shadow map 'follow' the player around the scene - Called
	//in StartFrame(). 
	void UpdateShadowMap(DirectionalLight* dLight);

private:
	//Make this class a singleton
	static GraphicsInterface* instance;
	//Hide constructor, destructor, copy constructor and assignment
	//operator
	GraphicsInterface();
	~GraphicsInterface();
	GraphicsInterface(GraphicsInterface& other);
	GraphicsInterface& operator=(GraphicsInterface& other);
};